local base = _G
local my   = {}
my.socket  = null
my.json    = null
my.ssl     = null
my.metat     = { __index = {} }
my.registr   = setmetatable({}, {__mode="k"})
my.req_https = null
my.req_http  = null


function attrdir(path)
    for file in my.lfs.dir(path) do
        if file ~= "." and file ~= ".." then
            local f = path..'/'..file
            local attr = my.lfs.attributes(f)
            if attr.mode == "directory" then
                attrdir(f)
            else
                LuaAuxLib.TracePrint(f)
            end
        end
    end
end

function char_to_hex(c)
    return string.format("%%%02X", string.byte(c))
end

function srequest(u, b)
    local t = {}
    local reqt = {
        url = u,
        sink = sink_table(t)
    }
    if b then
        reqt.source = source_string(b)
        reqt.headers = {
            ["content-length"] = string.len(b),
            ["content-type"] = "application/x-www-form-urlencoded"
        }
        reqt.method = "POST"
    end
    local code, headers, status = my.socket.skip(1, trequest(reqt))
    return table.concat(t), code, headers, status
end

function connect(address, port, laddress, lport)
    local sock, err = my.socket.tcp()
    if not sock then return nil, err end
    if laddress then
        local res, err = sock:bind(laddress, lport, -1)
        if not res then return nil, err end
    end
    local res, err = sock:connect(address, port)
    if not res then return nil, err end
    return sock
end

function bind(host, port, backlog)
    local sock, err = my.socket.tcp()
    if not sock then return nil, err end
    sock:setoption("reuseaddr", true)
    local res, err = sock:bind(host, port)
    if not res then return nil, err end
    res, err = sock:listen(backlog)
    if not res then return nil, err end
    return sock
end

function source_string(s)
    if s then
        local i = 1
        return function()
            local chunk = string.sub(s, i, i+2048-1)
            i = i + 2048
            if chunk ~= "" then return chunk
            else return nil end
        end
    else return source.empty() end
end

function sink_error(err)
    return function()
        return nil, err
    end
end
function sink_table(t)
    t = t or {}
    local f = function(chunk, err)
        if chunk then table.insert(t, chunk) end
        return 1
    end
    return f, t
end

function sink_file(handle, io_err)
    if handle then
        return function(chunk, err)
            if not chunk then
                handle:close()
                return 1
            else return handle:write(chunk) end
        end
    else return sink_error(io_err or "unable to open file") end
end

function source_rewind(src)
    base.assert(src)
    local t = {}
    return function(chunk)
        if not chunk then
            chunk = table.remove(t)
            if not chunk then return src()
            else return chunk end
        else
            table.insert(t, chunk)
        end
    end
end

function pump_step(src, snk)
    local chunk, src_err = src()
    local ret, snk_err = snk(chunk, src_err)
    if chunk and ret then return 1
    else return nil, src_err or snk_err end
end

function pump_all(src, snk, step)
    base.assert(src and snk)
    step = step or pump_step
    while true do
        local ret, err = step(src, snk)

        if not ret then
            if err then return nil, err
            else return 1 end
        end
    end
end

function base64decode(str)
    local key='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
    return ((str:gsub('.', function(x) 
        local r,key='',x:byte()
        for i=8,1,-1 do r=r..(key%2^i-key%2^(i-1)>0 and '1' or '0') end
        return r;
    end)..'0000'):gsub('%d%d%d?%d?%d?%d?', function(x)
        if (#x < 6) then return '' end
        local c=0
        for i=1,6 do c=c+(x:sub(i,i)=='1' and 2^(6-i) or 0) end
        return key:sub(c+1,c+1)
    end)..({ '', '==', '=' })[#str%3+1])
end

function choose(tables)
    return function(name, opt1, opt2)
        if base.type(name) ~= "string" then
            name, opt1, opt2 = "default", name, opt1
        end
        local f = tables[name or "nil"]
        if not f then base.error("unknown key (".. base.tostring(name) ..")", 3)
        else return f(opt1, opt2) end
    end
end

function get_sinkt()
    local sinkt = {}
    sinkt["close-when-done"] = function(sock)
        return base.setmetatable({
            getfd = function() return sock:getfd() end,
            dirty = function() return sock:dirty() end
        }, {
            __call = function(self, chunk, err)
                if not chunk then
                    sock:close()
                    return 1
                else return sock:send(chunk) end
            end
        })
    end
    sinkt["keep-open"] = function(sock)
        return base.setmetatable({
            getfd = function() return sock:getfd() end,
            dirty = function() return sock:dirty() end
        }, {
            __call = function(self, chunk, err)
                if chunk then return sock:send(chunk)
                else return 1 end
            end
        })
    end
    sinkt["http-chunked"] = function(sock)
        return base.setmetatable({
            getfd = function() return sock:getfd() end,
            dirty = function() return sock:dirty() end
        }, {
            __call = function(self, chunk, err)
                if not chunk then return sock:send("0\r\n\r\n") end
                local size = string.format("%X\r\n", string.len(chunk))
                return sock:send(size ..  chunk .. "\r\n")
            end
        })
    end
    sinkt["default"] = sinkt["keep-open"]
    return choose(sinkt)
end

function get_sourcet()
    local sourcet = {}
    sourcet["by-length"] = function(sock, length)
        return base.setmetatable({
            getfd = function() return sock:getfd() end,
            dirty = function() return sock:dirty() end
        }, {
            __call = function()
                if length <= 0 then return nil end
                local size = math.min(2048, length)
                local chunk, err = sock:receive(size)
                if err then return nil, err end
                length = length - string.len(chunk)
                return chunk
            end
        })
    end
    sourcet["until-closed"] = function(sock)
        local done
        return base.setmetatable({
            getfd = function() return sock:getfd() end,
            dirty = function() return sock:dirty() end
        }, {
            __call = function()
                if done then return nil end
                local chunk, err, partial = sock:receive(2048)

                if not err then return chunk
                elseif err == "closed" then
                    sock:close()
                    done = 1
                    return partial
                else return nil, err end
            end
        })
    end
    sourcet["http-chunked"] = function(sock, headers)
        return base.setmetatable({
            getfd = function() return sock:getfd() end,
            dirty = function() return sock:dirty() end
        }, {
            __call = function()
                local line, err = sock:receive("*l")
                if err then return nil, err end
                local size = base.tonumber(string.gsub(line, ";.*", ""), 16)
                if not size then return nil, "invalid chunk size" end
                if size > 0 then
                    local chunk, err, part = sock:receive(size)
                    if chunk then sock:receive("*l") end
                    return chunk, err
                else
                    headers, err = receiveheaders(sock, headers)
                    if not headers then return nil, err end
                end
            end
        })
    end
    sourcet["default"] = sourcet["until-closed"]
    return choose(sourcet)
end

function url_parse(url, default)
    local parsed = {}
    for i,v in base.pairs(default or parsed) do parsed[i] = v end
    if not url or url == "" then return nil, "invalid url" end
    url = string.gsub(url, "#(.*)$", function(f)
        parsed.fragment = f
        return ""
    end)
    url = string.gsub(url, "^([%w][%w%+%-%.]*)%:",
        function(s) parsed.scheme = s; return "" end)
    url = string.gsub(url, "^//([^/]*)", function(n)
        parsed.authority = n
        return ""
    end)
    url = string.gsub(url, "%?(.*)", function(q)
        parsed.query = q
        return ""
    end)
    url = string.gsub(url, "%;(.*)", function(p)
        parsed.params = p
        return ""
    end)
    if url ~= "" then 
        parsed.path = url 
    end
    local authority = parsed.authority
    if not authority then 
        return parsed 
    end
    authority = string.gsub(authority,"^([^@]*)@", function(u) 
        parsed.userinfo = u
        return "" 
    end)
    authority = string.gsub(authority, ":([^:]*)$", function(p) 
        parsed.port = p
        return "" 
    end)
    if authority ~= "" then 
        parsed.host = authority 
    end
    local userinfo = parsed.userinfo
    if not userinfo then 
        return parsed 
    end
    userinfo = string.gsub(userinfo, ":([^:]*)$", function(p) 
        parsed.password = p
        return ""
    end)
    parsed.user = userinfo
    return parsed
end

function unescape(s)
    return string.gsub(s, "%%(%x%x)", function(hex)
        return string.char(base.tonumber(hex, 16))
    end)
end

function make_set(t)
    local s = {}
    for i,v in base.ipairs(t) do
        s[t[i]] = 1
    end
    return s
end

function protect_segment(s)
    local segment_set = make_set {
        "-", "_", ".", "!", "~", "*", "'", "(",
        ")", ":", "@", "&", "=", "+", "$", ",",
    }
    return string.gsub(s, "([^A-Za-z0-9_])", function (c)
        if segment_set[c] then 
            return c
        else 
            return string.format("%%%02x", string.byte(c)) 
        end
    end)
end

function parse_path(path)
    local parsed = {}
    path = path or ""
    string.gsub(path, "([^/]+)", function (s) table.insert(parsed, s) end)
    for i = 1, #parsed do
        parsed[i] = unescape(parsed[i])
    end
    if string.sub(path, 1, 1) == "/" then 
        parsed.is_absolute = 1 
    end
    if string.sub(path, -1, -1) == "/" then
        parsed.is_directory = 1 
    end
    return parsed
end

function build_path(parsed, unsafe)
    local path = ""
    local n = #parsed
    if unsafe then
        for i = 1, n-1 do
            path = path .. parsed[i]
            path = path .. "/"
        end
        if n > 0 then
            path = path .. parsed[n]
            if parsed.is_directory then 
                path = path .. "/" 
            end
        end
    else
        for i = 1, n-1 do
            path = path .. protect_segment(parsed[i])
            path = path .. "/"
        end
        if n > 0 then
            path = path .. protect_segment(parsed[n])
            if parsed.is_directory then 
                path = path .. "/" 
            end
        end
    end
    if parsed.is_absolute then 
        path = "/" .. path 
    end
    return path
end

function url_build(parsed)
    local ppath = parse_path(parsed.path or "")
    local url = build_path(ppath)
    if parsed.params then 
        url = url .. ";" .. parsed.params 
    end
    if parsed.query then 
        url = url .. "?" .. parsed.query 
    end
    local authority = parsed.authority
    if parsed.host then
        authority = parsed.host
        if parsed.port then 
            authority = authority .. ":" .. parsed.port 
        end
        local userinfo = parsed.userinfo
        if parsed.user then
            userinfo = parsed.user
            if parsed.password then
                userinfo = userinfo .. ":" .. parsed.password
            end
        end
        if userinfo then 
            authority = userinfo .. "@" .. authority 
        end
    end
    if authority then 
        url = "//" .. authority .. url 
    end
    if parsed.scheme then 
        url = parsed.scheme .. ":" .. url 
    end
    if parsed.fragment then 
        url = url .. "#" .. parsed.fragment 
    end
    return url
end

function adjusturi(reqt)
    local u = reqt
    if not reqt.proxy and not PROXY then
        u = {
           path = my.socket.try(reqt.path, "invalid path 'nil'"),
           params = reqt.params,
           query = reqt.query,
           fragment = reqt.fragment
        }
    end
    return url_build(u)
end

function adjustproxy(reqt)
    local proxy = reqt.proxy or PROXY
    if proxy then
        proxy = url_parse(proxy)
        return proxy.host, proxy.port or 3128
    else
        return reqt.host, reqt.port
    end
end

function adjustheaders(reqt)
    local lower = {
        ["accept"] = "*/*",
        ["accept-encoding"] = "gzip, deflate",
        ["user-agent"] = "python-requests/2.25.1",
        ["host"] = reqt.host,
        ["connection"] = "keep-alive",
    }
    if reqt.user and reqt.password then
        lower["authorization"] =  "Basic " ..  (base64decode(reqt.user .. ":" .. reqt.password))
    end
    for i,v in base.pairs(reqt.headers or lower) do
        lower[string.lower(i)] = v
    end
    return lower
end

function adjustrequest(reqt)
    local default = {
        host = "",
        port = 80,
        path ="/",
        scheme = "http"
    }
    local nreqt = reqt.url and url_parse(reqt.url, default) or {}
    for i,v in base.pairs(reqt) do 
        nreqt[i] = v 
    end
    if nreqt.port == "" then 
        nreqt.port = 80 
    end
    my.socket.try(nreqt.host and nreqt.host ~= "", "invalid host '" .. base.tostring(nreqt.host) .. "'")
    nreqt.uri = reqt.uri or adjusturi(nreqt)
    nreqt.host, nreqt.port = adjustproxy(nreqt)
    nreqt.headers = adjustheaders(nreqt)
    return nreqt
end

function receiveheaders(sock, headers)
    local line, name, value, err
    headers = headers or {}
    line, err = sock:receive("*l")
    if err then return nil, err end
    while line ~= "" do
        name, value = my.socket.skip(2, string.find(line, "^(.-):%s*(.*)"))
        if not (name and value) then return nil, "malformed reponse headers" end
        name = string.lower(name)
        line, err  = sock:receive("*l")
        if err then return nil, err end
        while string.find(line, "^%s") do
            value = value .. line
            line = sock:receive("*l")
            if err then return nil, err end
        end
        if headers[name] then headers[name] = headers[name] .. ", " .. value
        else headers[name] = value end
    end
    return headers
end

function open(host, port, create)
    local c = my.socket.try((create or my.socket.tcp)())
    local h = base.setmetatable({ c = c }, my.metat)
    h.try = my.socket.newtry(function() h:close() end)
    h.try(c:settimeout(60))
    h.try(c:connect(host, port or 80))
    return h
end

function my.metat.__index:sendrequestline(method, uri)
    local reqline = string.format("%s %s HTTP/1.1\r\n", method or "GET", uri)
    return self.try(self.c:send(reqline))
end

function my.metat.__index:sendheaders(headers)
    local h = "\r\n"
    for i, v in base.pairs(headers) do
        h = i .. ": " .. v .. "\r\n" .. h
    end
    self.try(self.c:send(h))
    return 1
end

function my.metat.__index:sendbody(headers, source, step)
    source = source or nil
    step = step or pump_step
    local mode = "http-chunked"
    if headers["content-length"] then mode = "keep-open" end
    return self.try(pump_all(source, my.socket.sink(mode, self.c), step))
end

function my.metat.__index:receivestatusline()
    local status = self.try(self.c:receive(5))
    if status ~= "HTTP/" then return nil, status end
    status = self.try(self.c:receive("*l", status))
    local code = my.socket.skip(2, string.find(status, "HTTP/%d*%.%d* (%d%d%d)"))
    return self.try(base.tonumber(code), status)
end

function my.metat.__index:receiveheaders()
    return self.try(receiveheaders(self.c))
end

function my.metat.__index:receivebody(headers, sink, step)
    sink = sink or null
    step = step or pump_step
    local length = base.tonumber(headers["content-length"])
    local t = headers["transfer-encoding"]
    local mode = "default"
    if t and t ~= "identity" then mode = "http-chunked"
    elseif base.tonumber(headers["content-length"]) then mode = "by-length" end
    return self.try(pump_all(my.socket.source(mode, self.c, length), sink, step))
end

function my.metat.__index:receive09body(status, sink, step)
    local source = source_rewind(my.socket.source("until-closed", self.c))
    source(status)
    return self.try(pump_all(source, sink, step))
end

function my.metat.__index:close()
    return self.c:close()
end

function absolute_path(base_path, relative_path)
    if string.sub(relative_path, 1, 1) == "/" then return relative_path end
    local path = string.gsub(base_path, "[^/]*$", "")
    path = path .. relative_path
    path = string.gsub(path, "([^/]*%./)", function (s)
        if s ~= "./" then return s else return "" end
    end)
    path = string.gsub(path, "/%.$", "/")
    local reduced
    while reduced ~= path do
        reduced = path
        path = string.gsub(reduced, "([^/]*/%.%./)", function (s)
            if s ~= "../../" then return "" else return s end
        end)
    end
    path = string.gsub(reduced, "([^/]*/%.%.)$", function (s)
        if s ~= "../.." then return "" else return s end
    end)
    return path
end

function absolute(base_url, relative_url)
    if base.type(base_url) == "table" then
        base_parsed = base_url
        base_url = url_build(base_parsed)
    else
        base_parsed = url_parse(base_url)
    end
    local relative_parsed = url_parse(relative_url)
    if not base_parsed then return relative_url
    elseif not relative_parsed then return base_url
    elseif relative_parsed.scheme then return relative_url
    else
        relative_parsed.scheme = base_parsed.scheme
        if not relative_parsed.authority then
            relative_parsed.authority = base_parsed.authority
            if not relative_parsed.path then
                relative_parsed.path = base_parsed.path
                if not relative_parsed.params then
                    relative_parsed.params = base_parsed.params
                    if not relative_parsed.query then
                        relative_parsed.query = base_parsed.query
                    end
                end
            else    
                relative_parsed.path = absolute_path(base_parsed.path or "", relative_parsed.path)
            end
        end
        return url_build(relative_parsed)
    end
end


function shouldredirect(reqt, code, headers)
    return headers.location and
           string.gsub(headers.location, "%s", "") ~= "" and
           (reqt.redirect ~= false) and
           (code == 301 or code == 302) and
           (not reqt.method or reqt.method == "GET" or reqt.method == "HEAD") and
           (not reqt.nredirects or reqt.nredirects < 5)
end

function shouldreceivebody(reqt, code)
    if reqt.method == "HEAD" then return nil end
    if code == 204 or code == 304 then return nil end
    if code >= 100 and code < 200 then return nil end
    return 1
end

function tredirect(reqt, location)
    local result, code, headers, status = trequest {
        url = absolute(reqt.url, location),
        source = reqt.source,
        sink = reqt.sink,
        headers = reqt.headers,
        proxy = reqt.proxy, 
        nredirects = (reqt.nredirects or 0) + 1,
        create = reqt.create
    }
    headers = headers or {}
    headers.location = headers.location or location
    return result, code, headers, status
end

function trequest(reqt)
    local nreqt = adjustrequest(reqt)
    local h = open(nreqt.host, nreqt.port, nreqt.create)
    h:sendrequestline(nreqt.method, nreqt.uri)
    h:sendheaders(nreqt.headers)
    if nreqt.source then
        h:sendbody(nreqt.headers, nreqt.source, nreqt.step) 
    end
    local code, status = h:receivestatusline()
    if not code then
        h:receive09body(status, nreqt.sink, nreqt.step)
        return 1, 200
    end
    local headers
    while code == 100 do 
        headers = h:receiveheaders()
        code, status = h:receivestatusline()
    end
    headers = h:receiveheaders()
    if shouldredirect(nreqt, code, headers) and not nreqt.source then
        h:close()
        return tredirect(reqt, headers.location)
    end
    if shouldreceivebody(nreqt, code) then
        h:receivebody(headers, nreqt.sink, nreqt.step)
    end
    h:close()
    return 1, code, headers, status
end

function http_requests()
    local request = my.socket.protect(function(reqt, body)
        if base.type(reqt) == "string" then 
            return srequest(reqt, body)
        else 
            return trequest(reqt) 
        end
    end)
    return request
end

--[re.search(".*\-",i).group()[:-1]] + re.findall("\d+",i[len(re.search(".*\-",i).group()):]) if 57 > ord(i[len(re.search(".*\-",i).group()):][0]) > 48 else [i]

-- https -- ssl --


function optexec(func, param, ctx)
  if param then
    if type(param) == "table" then
      return func(ctx, table.unpack(param))
    else
      return func(ctx, param)
    end
  end
  return true
end

function array2wireformat(array)
   local str = ""
   for k, v in ipairs(array) do
      if type(v) ~= "string" then return nil end
      local len = #v
      if len == 0 then
        return nil, "invalid ALPN name (empty string)"
      elseif len > 255 then
        return nil, "invalid ALPN name (length > 255)"
      end
      str = str .. string.char(len) .. v
   end
   if str == "" then return nil, "invalid ALPN list (empty)" end
   return str
end

function wireformat2array(str)
   local i = 1
   local array = {}
   while i < #str do
      local len = str:byte(i)
      array[#array + 1] = str:sub(i + 1, i + len)
      i = i + len + 1
   end
   return array
end

function newcontext(cfg)
   local succ, msg, ctx
   ctx, msg = my.ssl.context.create(cfg.protocol)
   if not ctx then return nil, msg end
   succ, msg = my.ssl.context.setmode(ctx, cfg.mode)
   if not succ then return nil, msg end
   local certificates = cfg.certificates
   if not certificates then
      certificates = {
         { certificate = cfg.certificate, key = cfg.key, password = cfg.password }
      }
   end
   for _, certificate in ipairs(certificates) do
      if certificate.key then
         if certificate.password and
            type(certificate.password) ~= "function" and
            type(certificate.password) ~= "string"
         then
            return nil, "invalid password type"
         end
         succ, msg = my.ssl.context.loadkey(ctx, certificate.key, certificate.password)
         if not succ then return nil, msg end
      end
      if certificate.certificate then
        succ, msg = my.ssl.context.loadcert(ctx, certificate.certificate)
        if not succ then return nil, msg end
        if certificate.key and my.ssl.context.checkkey then
          succ = my.ssl.context.checkkey(ctx)
          if not succ then return nil, "private key does not match public key" end
        end
      end
   end
   if cfg.cafile or cfg.capath then
      succ, msg = my.ssl.context.locations(ctx, cfg.cafile, cfg.capath)
      if not succ then return nil, msg end
   end
   if cfg.ciphers then
      succ, msg = my.ssl.context.setcipher(ctx, cfg.ciphers)
      if not succ then return nil, msg end
   end
   if cfg.ciphersuites then
      succ, msg = my.ssl.context.setciphersuites(ctx, cfg.ciphersuites)
      if not succ then return nil, msg end
   end
   succ, msg = optexec(my.ssl.context.setverify, cfg.verify, ctx)
   if not succ then return nil, msg end
   succ, msg = optexec(my.ssl.context.setoptions, cfg.options, ctx)
   if not succ then return nil, msg end
   if cfg.depth then
      succ, msg = my.ssl.context.setdepth(ctx, cfg.depth)
      if not succ then return nil, msg end
   end
   if cfg.dhparam then
      if type(cfg.dhparam) ~= "function" then
         return nil, "invalid DH parameter type"
      end
      my.ssl.context.setdhparam(ctx, cfg.dhparam)
   end
   if (not my.ssl.config.algorithms.ec) and (cfg.curve or cfg.curveslist) then
     return false, "elliptic curves not supported"
   end
   if my.ssl.config.capabilities.curves_list and cfg.curveslist then
     succ, msg = my.ssl.context.setcurveslist(ctx, cfg.curveslist)
     if not succ then return nil, msg end
   elseif cfg.curve then
     succ, msg = my.ssl.context.setcurve(ctx, cfg.curve)
     if not succ then return nil, msg end
   end
   if cfg.verifyext and ctx.setverifyext then
      succ, msg = optexec(ctx.setverifyext, cfg.verifyext, ctx)
      if not succ then return nil, msg end
   end
   if cfg.mode == "server" and cfg.alpn then
      if type(cfg.alpn) == "function" then
         local alpncb = cfg.alpn
         succ, msg = my.ssl.context.setalpncb(ctx, function(str)
            local protocols = alpncb(wireformat2array(str))
            if type(protocols) == "string" then
               protocols = { protocols }
            elseif type(protocols) ~= "table" then
               return nil
            end
            return (array2wireformat(protocols))    
         end)
         if not succ then return nil, msg end
      elseif type(cfg.alpn) == "table" then
         local protocols = cfg.alpn
         succ, msg = array2wireformat(protocols)
         if not succ then return nil, msg end
         succ, msg = my.ssl.context.setalpncb(ctx, function()
            return (array2wireformat(protocols))   
         end)
         if not succ then return nil, msg end
      else
         return nil, "invalid ALPN parameter"
      end
   elseif cfg.mode == "client" and cfg.alpn then
      local alpn
      if type(cfg.alpn) == "string" then
         alpn, msg = array2wireformat({ cfg.alpn })
      elseif type(cfg.alpn) == "table" then
         alpn, msg = array2wireformat(cfg.alpn)
      else
         return nil, "invalid ALPN parameter"
      end
      if not alpn then return nil, msg end
      succ, msg = my.ssl.context.setalpn(ctx, alpn)
      if not succ then return nil, msg end
   end
   if my.ssl.config.capabilities.dane and cfg.dane then
      if type(cfg.dane) == "table" then
         my.ssl.context.setdane(ctx, table.unpack(cfg.dane))
      else
         my.ssl.context.setdane(ctx)
      end
   end
   return ctx
end

function ssl_wrap(sock, cfg)
   local ctx, msg
   if type(cfg) == "table" then
      ctx, msg = newcontext(cfg)
      if not ctx then return nil, msg end
   else
      ctx = cfg
   end
   local s, msg = my.ssl.core.create(ctx)
   if s then
      my.ssl.core.setfd(s, sock:getfd())
      sock:setfd(my.ssl.core.SOCKET_INVALID)
      my.registr[s] = ctx
      return s
   end
   return nil, msg 
end

function info(ssl, field)
  local str, comp, err, protocol
  comp, err = my.ssl.core.compression(ssl)
  if err then
    return comp, err
  end
  if field == "compression" then
    return comp
  end
  local info = {compression = comp}
  str, info.bits, info.algbits, protocol = my.ssl.core.info(ssl)
  if str then
    info.cipher, info.protocol, info.key,
    info.authentication, info.encryption, info.mac = string.match(str, "^(%S+)%s+(%S+)%s+Kx=(%S+)%s+Au=(%S+)%s+Enc=(%S+)%s+Mac=(%S+)")
    info.export = (string.match(str, "%sexport%s*$") ~= nil)
  end
  if protocol then
    info.protocol = protocol
  end
  if field then
    return info[field]
  end
  return ( (next(info)) and info )
end


function urlstring_totable(url, body, result_table)
   local murl = {
      url = url_build(url_parse(url, {port = 443})),
      method = body and "POST" or "GET",
      sink = sink_table(result_table)
   }
   if body then
      murl.source = source_string(body)
      murl.headers = {
         ["content-length"] = #body,
         ["content-type"] = "application/x-www-form-urlencoded",
      }
   end
   return murl
end

function reg(conn)
   local mt = getmetatable(conn.sock).__index
   for name, method in pairs(mt) do
      if type(method) == "function" then
         conn[name] = function (self, ...)
                         return method(self.sock, ...)
                      end
      end
   end
end

function tcp(params)
    local cfg = {
        protocol = "any",
        options  = {"all", "no_sslv2", "no_sslv3"},
        verify   = "none",
    }
   params = params or {}
   for k, v in pairs(cfg) do 
      params[k] = params[k] or v
   end
   params.mode = "client"
   return function ()
      local conn = {}
      conn.sock = my.socket.try(my.socket.tcp())
      local st = getmetatable(conn.sock).__index.settimeout
      function conn:settimeout(...)
         return st(self.sock, ...)
      end
      function conn:connect(host, port)
         my.socket.try(self.sock:connect(host, port))
         self.sock = my.socket.try(ssl_wrap(self.sock, params))
         self.sock:sni(host)
         my.socket.try(self.sock:dohandshake())
         reg(self, getmetatable(self.sock))
         return 1
      end
      return conn
  end
end

function https_requests(murl, body)
    my.ssl.core.setmethod("info", info)
    local result_table = {}
    local stringrequest = type(murl) == "string"
    if stringrequest then
        murl = urlstring_totable(murl, body, result_table)
    else
        murl.url = url_build(url_parse(murl.url, {port = 443}))
    end
    if PROXY or murl.proxy then
        return nil, "proxy not supported"
    elseif murl.redirect then
        return nil, "redirect not supported"
    elseif murl.create then
        return nil, "create function not permitted"
    end
    murl.create = tcp(murl)
    local res, code, headers, status = my.req_http(murl)
    if res and stringrequest then
        return table.concat(result_table), code, headers, status
    end
    return res, code, headers, status
end

function urlencode(url)
    if url == nil then
        return
    end
    url = url:gsub("\n", "\r\n")
    url = url:gsub("([^%w _%%%-%.~])", char_to_hex)
    url = url:gsub(" ", "+")
    return url
end

function file_exists(filename)
    local file = io.open(filename, "r")
    if file then
        file:close()
        return true
    else
        return false
    end
end

function QMPlugin.upload_file(url, filename, array)
    local boundary = "abcd"
    local body = {}
    table.insert(body, "--" .. boundary .. "\r\n")
    if array then
        for k, v in pairs(array) do
            if file_exists(v) then
                table.insert(body, 'Content-Disposition: form-data; name="'..k..'"; filename="'.. v ..'"\r\n')
                table.insert(body, "Content-Type: text/plain\r\n\r\n")
                local fileHandle = io.open(filename,"rb") 
                local fileContent = fileHandle:read( "*a" )
                fileHandle:close()
                table.insert(body, fileContent.."\r\n")
                table.insert(body, "--" .. boundary .. "\r\n")
            else
                table.insert(body, 'Content-Disposition: form-data; name="'..k..'"\r\n\r\n')
                table.insert(body, v.."\r\n")
                table.insert(body, "--" .. boundary .. "\r\n")
            end
        end
    end
    local response_body = { }
    local bodyfile = table.concat(body)
    local _, code = my.req_http{
        url = url , 
        method = "POST",
        headers = {
            ["Content-Type"] = "multipart/form-data; boundary=" .. boundary,
            ["Content-Length"] = bodyfile:len()
        },
        source = source_string(bodyfile) ,         
        sink = sink_table(response_body),
    }
    return table.concat(response_body)
end

function QMPlugin.http_form(url, form)
    local res = {}
    local request_body = ""
    for k, v in pairs(form) do
        request_body = request_body..k.."="..v.."&"
    end
    local body, code, headers, status = my.req_http{
        method = "POST",
        url = url,
        headers = { 
            ["Content-Type"] = "application/x-www-form-urlencoded",
            ["Content-Length"] =  #request_body
        },
        source = source_string(request_body),
        sink = sink_table(res),
    }
    return table.concat(res)
end


function QMPlugin.download_file(url, name)
    local urls = url..urlencode(name)
    local body, code, headers, status = my.req_http{
        method = "GET",
        url = urls,
        sink = sink_file(io.open("/sdcard/Download/"..name, "w")),
    }
    return "/sdcard/Download/"..name
end
function QMPlugin.init_http(path)
        --LuaAuxLib.TracePrint(path.."ssl.so")
        --attrdir("/usr/local/lib/lua/5.2")
        --package.path =  path.."?.lua;"..package.path
        --LuaAuxLib.TracePrint(package.cpath)
        --package.cpath =  path.."openssl.so;"..package.cpath
        --LuaAuxLib.TracePrint(package.cpath)
        --my.json          = require("cjson")
        my.socket        = require("socket")
        my.lfs           = require("lfs")
        my.socket.try    = my.socket.newtry()
        my.socket.sink   = get_sinkt() 
        my.socket.source = get_sourcet()
        my.req_http      = http_requests()
        my.req_https     = https_requests
        --os.execute("uname -a")
        --attrdir(path)
        --LuaAuxLib.TracePrint(package.path)
        --my.ssl      = require("openssl")
        --LuaAuxLib.TracePrint(my.ssl)
        --LuaAuxLib.TracePrint(package.path)
        --LuaAuxLib.TracePrint(my.core())
        --my.ssl.context   = require("ssl.context")
        --my.ssl.config    = require("ssl.config")
    --my.socket.connect = connect
    --my.socket.bind    = bind

end

function QMPlugin.CopyIcon(oldPath,newPath)
    local oldIcon = io.open(oldPath,"rb")
    local data = oldIcon:read("*all")
    oldIcon:close()
    local newIcon = io.open(newPath,"wb")
    newIcon:write(data)
    newIcon:close()
end